package com.boarsoft.flow.demo;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.alibaba.fastjson.JSON;
import com.boarsoft.common.util.JsonUtil;
import com.boarsoft.flow.core.SimpleFlow;
import com.boarsoft.flow.core.SimpleFlowEngine;
import com.boarsoft.flow.core.SimpleFlowEngineImpl;
import com.boarsoft.flow.demo.bean.User;
import com.boarsoft.flow.demo.login.LoginHard;
import com.boarsoft.flow.event.SimpleFlowListener;
import com.boarsoft.flow.event.SimpleFlowListenerImpl;

public class Main {
	private static final Logger log = LoggerFactory.getLogger(Main.class);

	public static void main(String[] args) throws Exception {

		// Thread.sleep(10000L);

		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:conf/context.xml");
		ExecutorService threadPool = (ExecutorService) ctx.getBean("threadPool");
		int tc = 1, len = 10000;
		// int tc = 1, len = 1;
		ExecutorService es = Executors.newFixedThreadPool(tc);
		SimpleFlowEngine fe = (SimpleFlowEngine) ctx.getBean("simpleFlowEngine");
		final LoginHard lh = new LoginHard(threadPool);
		final User u = new User();
		u.setName("Mac_J");
		u.setPassword("123");

		try {
			long l = System.currentTimeMillis();
			SimpleFlow fo = fe.start("login", u);
			log.info("Flow trace {}:\n{}\n", fo.getId(), JsonUtil.from(fo.getTrace()));
			System.out.println(String.format("Flow engine take %dms", System.currentTimeMillis() - l));
			//
			l = System.currentTimeMillis();
			Future<Long> ft = es.submit(new Callable<Long>() {
				@Override
				public Long call() throws Exception {
					return lh.start(u);
				}
			});
			ft.get(3000L, TimeUnit.MILLISECONDS);
			System.out.println(String.format("Hard code take %dms", System.currentTimeMillis() - l));
		} catch (Throwable e) {
			log.error("Error on call LoginHard.start()", e);
		}

		try {
			basicTest1(fe);
			// // 由tc个线程发起压力，每个压力线程串行执行len次，每次执行都通过threadPool在业务线程中执行，并由流程引擎负责计时
			// flowPerfTest1(fe, u, es, threadPool, tc, len);
			// //
			// 由tc个线程发起压力，每个压力线程串行执行len次，每次执行都通过threadPool在业务线程中执行，并用Future处理超时
			// hardPerfTest1(lh, u, es, threadPool, tc, len);
			// // 由主线程调用流程引擎发起tc*len个流程，流程引擎在600大小的固定线程池上执行这些流程，并等待所有流程执行完毕后结束
			// flowPerfTest2(fe, u, tc, len);
			// // 由主线程通过600大小的固定线程池发起tc*len次调用，并在所有调用执行完成后结束
			// hardPerfTest2(lh, u, threadPool, tc, len);
		} catch (Throwable e) {
			e.printStackTrace();
		} finally {
			es.shutdown();
			if (ctx != null) {
				ctx.close();
			}
		}
		System.exit(0);
	}

	public static void hardPerfTest2(final LoginHard lh, final User u, final ExecutorService threadPool, final int tc,
			final int len) throws Throwable {
		final CountDownLatch cdl = new CountDownLatch(tc);
		long l = System.currentTimeMillis();
		for (int n = 0; n < tc * len; n++) {
			threadPool.execute(new Runnable() {
				@Override
				public void run() {
					try {
						lh.start(u);
					} catch (InterruptedException e) {
						e.printStackTrace();
					} finally {
						cdl.countDown();
					}
				}
			});
		}
		cdl.await();
		System.out.println(String.format("Hard code take %dms", System.currentTimeMillis() - l));
	}

	public static void flowPerfTest2(final SimpleFlowEngine fe, final User u, final int tc, final int len) throws Throwable {
		final CountDownLatch cdl = new CountDownLatch(tc);
		SimpleFlowEngineImpl fei = (SimpleFlowEngineImpl) fe;
		fei.setListener(new SimpleFlowListener() {

			@Override
			public void onError(SimpleFlow flow) {
				cdl.countDown();
			}

			@Override
			public void onSuspend(SimpleFlow flow) {
				// TODO Auto-generated method stub

			}

			@Override
			public void onStop(SimpleFlow flow) {
				cdl.countDown();
			}

			@Override
			public void onTimeout(SimpleFlow flow) {
				cdl.countDown();
			}

			@Override
			public void onBegin(SimpleFlow flow) {
				// TODO Auto-generated method stub

			}

			@Override
			public void onEnd(SimpleFlow flow) {
				cdl.countDown();
			}

			@Override
			public void onStep(SimpleFlow flow) {
				// TODO Auto-generated method stub

			}

		});

		long l = System.currentTimeMillis();
		for (int i = 0; i < tc * len; i++) {
			fe.execute("login", null, u, null);
		}
		cdl.await();
		System.out.println(String.format("Flow engine take %dms", System.currentTimeMillis() - l));

		fei.setListener(new SimpleFlowListenerImpl());
	}

	public static void flowPerfTest1(final SimpleFlowEngine fe, final User u, final ExecutorService es,
			final ExecutorService threadPool, int tc, final int len) throws Throwable {
		final CountDownLatch cdl = new CountDownLatch(tc);
		long l = System.currentTimeMillis();
		try {
			for (int n = 0; n < tc; n++) {
				es.execute(new Runnable() {
					public void run() {
						for (int i = 0; i < len; i++) {
							try {
								fe.start("login", u);
							} catch (Throwable e) {
								log.error("Error on call flow login", e);
							}
							// try {
							// Future<Long> ft = threadPool.submit(new
							// Callable<Long>() {
							// @Override
							// public Long call() throws Exception {
							// SimpleFlow f;
							// try {
							// f = fe.start("login", u);
							// return 0L; // TODO
							// } catch (Throwable e) {
							// e.printStackTrace();
							// }
							// return -1L;
							// }
							// });
							// ft.get(3000L, TimeUnit.MILLISECONDS);
							// } catch (InterruptedException |
							// ExecutionException | TimeoutException e) {
							// e.printStackTrace();
							// }
						}
						cdl.countDown();
					}
				});
			}
			cdl.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(String.format("Flow engine take %dms", System.currentTimeMillis() - l));
	}

	public static void hardPerfTest1(final LoginHard lh, final User u, final ExecutorService es,
			final ExecutorService threadPool, int tc, final int len) throws Throwable {
		final CountDownLatch cdl = new CountDownLatch(tc);
		long l = System.currentTimeMillis();
		try {
			for (int n = 0; n < tc; n++) {
				es.execute(new Runnable() {
					public void run() {
						for (int i = 0; i < len; i++) {
							try {
								lh.start(u);
								// Future<Long> ft = threadPool.submit(new
								// Callable<Long>() {
								// @Override
								// public Long call() throws Exception {
								// return lh.start(u);
								// }
								// });
								// ft.get(3000L, TimeUnit.MILLISECONDS);
							} catch (Throwable e) {
								log.error("Error on call LoginHard.start()", e);
							}
						}
						cdl.countDown();
					}
				});
			}
			cdl.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(String.format("Hard code take %dms", System.currentTimeMillis() - l));
	}

	public static void basicTest1(SimpleFlowEngine fe) throws Throwable {
		User u = new User();
		u.setName("Mac_J");
		u.setPassword("123");
		SimpleFlow f1 = fe.start("login", u);
		log.info(JsonUtil.from(f1.getData()));

		// SimpleFlow f2 = fe.start("accountQry",
		// "00000000000000000000000000000000");
		// System.out.println(JsonUtil.from(f.getData()));

		Map<String, Object> pm = new HashMap<String, Object>();
		pm.put("userId", u.getId());
		pm.put("work", 3);
		SimpleFlow f3 = fe.start("makeMoney", pm);
		System.out.println(JsonUtil.from(f3.getData()));
		System.out.println(JsonUtil.from(f3.getTrace()));
	}
}
